﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Web;
using System.Web.Configuration;
using System.Web.Security;
using System.Web.SessionState;
using Itfort;

namespace Users.Sso
{
	/// <summary>
	/// 
	/// </summary>
	public class SsoAuthentication
	{
		static HttpSessionState Session
		{
			get { return HttpContext.Current.Session; }
		}

		static HttpRequest Request
		{
			get { return HttpContext.Current.Request; }
		}

		static HttpResponse Response
		{
			get { return HttpContext.Current.Response; }
		}

		/// <summary>
		/// 
		/// </summary>
		public static string SsoCookieName { get; private set; }

		static SsoAuthentication()
		{
			SsoCookieName = ".ticket";
		}

		/// <summary>
		/// 获取用户标识。
		/// 该标识从Cookie中还原。
		/// </summary>
		/// <returns></returns>
		public static string GetIdentity()
		{
			var ticket = GetTicketFromCookie();
			if (ticket == null) return null;
			if (!VerifyTicket(ticket)) return null;
			return ticket.Identity;
		}

		/// <summary>
		/// 为提供的用户标识创建一个身份验证票证，但不输出。
		/// </summary>
		/// <param name="identity"></param>
		/// <returns></returns>
		static HttpCookie GetAuthCookie(string identity)
		{
			SsoAuthenticationTicket ticket = NewSsoAuthenticationTicket(identity);
			string cookieValue = Encrypt(ticket);
			HttpCookie ticketCookie = new HttpCookie(SsoCookieName);
			ticketCookie.Value = cookieValue;
			ticketCookie.Path = "/";
			ticketCookie.Domain = FormsAuthentication.CookieDomain;
			// 浏览器进程内有效
			return ticketCookie;
		}

		/// <summary>
		/// 为提供的用户标识创建一个身份验证票证，并将该票证添加到响应的Cookie集合中。
		/// </summary>
		/// <param name="identity">用户标识</param>
		/// <param name="duration">有效时间</param>
		public static void SetAuthCookie(string identity, TimeSpan duration)
		{
			HttpCookie ticketCookie = GetAuthCookie(identity);
			ticketCookie.Expires = DateTime.Now.Add(duration);
			Response.Cookies.Add(ticketCookie);
		}

		/// <summary>
		/// 清空认证Cookie。
		/// </summary>
		public static void ClearAuthCookie()
		{
			HttpCookie ticketCookie = new HttpCookie(SsoCookieName);
			ticketCookie.Value = null;
			ticketCookie.Path = "/";
			ticketCookie.Domain = FormsAuthentication.CookieDomain;
			ticketCookie.Expires = DateTime.Parse("1900-01-01");
			Response.Cookies.Add(ticketCookie);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public static SsoAuthenticationTicket GetTicketFromCookie()
		{
			HttpCookie ticketCookie = Request.Cookies[SsoCookieName];
			if (ticketCookie == null) return null;
			string cookieValue = ticketCookie.Value;

			try
			{
				return Decrypt(cookieValue);
			}
			catch
			{
				// 需要记录日志
				return null;
			}
		}

		static SsoAuthenticationTicket NewSsoAuthenticationTicket(string identity)
		{
			Guid token = Comb.NewComb();
			DateTime expires = DateTime.Now.AddDays(1); // 浏览器进程内有效，Token有效期为一天
			AuthTicket authTicket = UsersSf.SsoService.CreateAuthTicket(token, identity, expires);
			SsoAuthenticationTicket ticket = new SsoAuthenticationTicket();
			ticket.Token = authTicket.Token.ToString();
			ticket.Identity = authTicket.Identity;
			ticket.Expires = authTicket.Expires;
			return ticket;
		}

		static string Encrypt(SsoAuthenticationTicket ticket)
		{
			BinaryFormatter bf = new BinaryFormatter();
			MemoryStream ms = new MemoryStream();
			bf.Serialize(ms, ticket);
			byte[] buffer = ms.ToArray();
			ms.Close();
			return Utility.AesEncryptAndToBase64(buffer);
		}

		static SsoAuthenticationTicket Decrypt(string encryptedTicket)
		{
			byte[] buffer = Utility.AesDecryptFromBase64(encryptedTicket);

			BinaryFormatter bf = new BinaryFormatter();
			using (MemoryStream ms = new MemoryStream(buffer))
			{
				ms.Position = 0;
				return bf.Deserialize(ms) as SsoAuthenticationTicket;
			}
		}

		static bool VerifyTicket(SsoAuthenticationTicket ticket)
		{
			// 令牌过期（客户端传回值判断）
			if (ticket.Expires < DateTime.Now) return false;
			// 校验票证
			Guid token = new Guid(ticket.Token);
			AuthTicket authTicket = UsersSf.SsoService.GetAuthTicket(token);
			// 无效令牌
			if (authTicket == null) return false;
			// 冒用令牌
			if (ticket.Identity != authTicket.Identity) return false;
			// 令牌过期（服务端保存值判断）
			if (authTicket.Expires < DateTime.Now) return false;

			return true;
		}
	}
}
